home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Resources / Online / Term / Extras / Source / term-source.lha / XPR.c < prev    next >
C/C++ Source or Header  |  1996-10-20  |  54KB  |  2,585 lines

  1. /*
  2. **    XPR.c
  3. **
  4. **    External transfer protocol support routines
  5. **
  6. **    Copyright © 1990-1996 by Olaf `Olsen' Barthel
  7. **        All Rights Reserved
  8. **
  9. **    :ts=4
  10. */
  11.  
  12. #ifndef _GLOBAL_H
  13. #include "Global.h"
  14. #endif
  15.  
  16.     /* Prototypes for local routines. */
  17.  
  18. STATIC BOOL CheckForCarrierLoss(UWORD SerialStatus);
  19. STATIC VOID HandleCarrierLoss(VOID);
  20. STATIC VOID PrintBox(LONG Box, LONG Line, STRPTR String, ...);
  21. STATIC BOOL OpenTransferWindow(VOID);
  22. STATIC LONG CheckAbort(BOOL CheckCarrier);
  23. STATIC STRPTR NewFileName(STRPTR Source, STRPTR Buffer, LONG BufferSize);
  24. STATIC VOID IdentifySource(STRPTR Name, STRPTR BBSName, struct DateStamp *OpenDate);
  25. STATIC LONG GetSeconds(STRPTR String);
  26. STATIC STRPTR TruncateName(STRPTR FileName);
  27. STATIC BOOL IsBlockMappedDevice(struct MsgPort *Handler);
  28. STATIC LONG CalculateBlocks(LONG Size, LONG BlockSize);
  29. STATIC BYTE SerialErrorReport(struct IOExtSer *Request);
  30.  
  31.     /* These variables keep the transferred bytes and transfer
  32.      * time in seconds.
  33.      */
  34.  
  35. STATIC LONG                ByteVal,ByteMax,
  36.                         TimeVal,TimeMax;
  37.  
  38. STATIC LONG                LastTimeDelta,LastPercent;
  39.  
  40.     /* Transfer statistics. */
  41.  
  42. STATIC ULONG            CPS_Minimum,
  43.                         CPS_Maximum,
  44.                         CPS_Average,
  45.                         CPS_Current,
  46.                         CPS_Count,
  47.                         CPS_Last;
  48.  
  49.     /* The file currently being transferred. */
  50.  
  51. STATIC struct Buffer    *CurrentFile;
  52.  
  53.     /* Yet some more flags. */
  54.  
  55. STATIC BOOL                UserWasNotified,            /* User was notified that the file being received will not fit. */
  56.                         CheckSpace,                    /* Check the space left on the destination device? */
  57.                         FileSysTypeWasChecked,        /* Filing system type was checked? */
  58.                         ErrorMessageWasDisplayed,    /* The error message was displayed? */
  59.                         CarrierLossWasDisplayed;    /* The loss of the carrier signal was displayed? */
  60.  
  61.     /* CheckForCarrierLoss(UWORD SerialStatus):
  62.      *
  63.      *    Examine the serial status word and do whatever
  64.      *    may be necessary next.
  65.      */
  66.  
  67. STATIC BOOL
  68. CheckForCarrierLoss(UWORD SerialStatus)
  69. {
  70.     if((SerialStatus & CIAF_COMCD) && Config->SerialConfig->CheckCarrier && !Config->SerialConfig->DirectConnection)
  71.     {
  72.         HandleCarrierLoss();
  73.  
  74.         return(TRUE);
  75.     }
  76.     else
  77.         return(FALSE);
  78. }
  79.  
  80.     /* HandleCarrierLoss():
  81.      *
  82.      *    Do whatever needs to be done in case the carrier signal
  83.      *    is lost during the transmission.
  84.      */
  85.  
  86. STATIC VOID
  87. HandleCarrierLoss()
  88. {
  89.     if(!CarrierLossWasDisplayed)
  90.     {
  91.         CarrierLossWasDisplayed = TRUE;
  92.  
  93.         AddTransferInfo(TRUE,LocaleString(MSG_TERMXPR_CARRIER_LOST_TXT));
  94.     }
  95.  
  96.     SetOnlineState(FALSE);
  97.  
  98.     TransferError = TRUE;
  99. }
  100.  
  101.     /* PrintBox(LONG Box,LONG Line,STRPTR String,...):
  102.      *
  103.      *    Update the contents of a text box.
  104.      */
  105.  
  106. STATIC VOID
  107. PrintBox(LONG Box,LONG Line,STRPTR String,...)
  108. {
  109.     UBYTE LocalBuffer[256];
  110.     va_list VarArgs;
  111.  
  112.     va_start(VarArgs,String);
  113.     LimitedVSPrintf(sizeof(LocalBuffer),LocalBuffer,String,VarArgs);
  114.     va_end(VarArgs);
  115.  
  116.     LT_SetAttributes(TransferHandle,Box,
  117.         LABX_Index,    Line,
  118.         LABX_Text,    LocalBuffer,
  119.     TAG_DONE);
  120. }
  121.  
  122.     /* OpenTransferWindow():
  123.      *
  124.      *    Local routine to open the transfer window.
  125.      */
  126.  
  127. STATIC BOOL
  128. OpenTransferWindow()
  129. {
  130.         /* Block window input. */
  131.  
  132.     BlockWindows();
  133.  
  134.         /* Try to open the transfer window panel. */
  135.  
  136.     if(TransferPanel(LocaleString(MSG_TERMXPR_RECEIVE_FILES_TXT)))
  137.     {
  138.             /* Supply the log text. */
  139.  
  140.         LogAction(LocaleString(MSG_TERMXPR_LOGMSG_INITIATE_BINARY_DOWNLOAD_TXT));
  141.  
  142.             /* Return success. */
  143.  
  144.         return(TRUE);
  145.     }
  146.     else
  147.     {
  148.             /* Re-enable window input. */
  149.  
  150.         ReleaseWindows();
  151.  
  152.             /* Return failure. */
  153.  
  154.         return(FALSE);
  155.     }
  156. }
  157.  
  158.     /* CheckAbort(BOOL CheckCarrier):
  159.      *
  160.      *    Check for transfer abort.
  161.      */
  162.  
  163. STATIC LONG
  164. CheckAbort(BOOL CheckCarrier)
  165. {
  166.     struct IntuiMessage    *Message;
  167.     ULONG MsgClass,MsgQualifier;
  168.     struct Gadget *MsgGadget;
  169.     struct Window *MsgWindow;
  170.     UWORD MsgCode;
  171.     LONG Result;
  172.  
  173.     Result = 0;
  174.  
  175.         /* No window available for output? */
  176.  
  177.     if(!TransferWindow)
  178.     {
  179.         if(!OpenTransferWindow())
  180.             return(-1);
  181.     }
  182.  
  183.         /* Are we to check the carrier status? */
  184.  
  185.     if(CheckCarrier)
  186.     {
  187.             /* Return error if carrier is lost. */
  188.  
  189.         if(GetSerialStatus() & CIAF_COMCD)
  190.         {
  191.             HandleCarrierLoss();
  192.  
  193.             return(-1);
  194.         }
  195.     }
  196.  
  197.         /* Process all incoming messages. */
  198.  
  199.     while(Message = (struct IntuiMessage *)GT_GetIMsg(TransferWindow->UserPort))
  200.     {
  201.         MsgClass        = Message->Class;
  202.         MsgQualifier    = Message->Qualifier;
  203.         MsgCode            = Message->Code;
  204.         MsgGadget        = (struct Gadget *)Message->IAddress;
  205.         MsgWindow        = Message->IDCMPWindow;
  206.  
  207.         GT_ReplyIMsg(Message);
  208.  
  209.         if(MsgWindow == TransferWindow)
  210.         {
  211.             if(!Result)
  212.             {
  213.                 LT_HandleInput(TransferHandle,MsgQualifier,&MsgClass,&MsgCode,&MsgGadget);
  214.  
  215.                 if(MsgClass == IDCMP_GADGETUP)
  216.                 {
  217.                     switch(MsgGadget->GadgetID)
  218.                     {
  219.                         case GAD_TRANSFER_ABORT:
  220.  
  221.                             LogAction(LocaleString(MSG_TERMXPR_LOGMSG_TRANSFER_ABORTED_TXT));
  222.  
  223.                             TransferAborted = TRUE;
  224.  
  225.                             Result = -1;
  226.  
  227.                             break;
  228.  
  229.                         case GAD_TRANSFER_SKIP:
  230.  
  231.                             LogAction(LocaleString(MSG_TERMXPR_LOGMSG_FILE_SKIPPED_TXT));
  232.  
  233.                             Result = 1;
  234.  
  235.                             break;
  236.  
  237.                         case GAD_TRANSFER_ABORT_FILE:
  238.  
  239.                             LogAction(LocaleString(MSG_TERMXPR_LOGMSG_FILE_ABORTED_TXT));
  240.  
  241.                             Result = 2;
  242.  
  243.                             break;
  244.                     }
  245.                 }
  246.             }
  247.         }
  248.     }
  249.  
  250.     return(Result);
  251. }
  252.  
  253.     /* NewFileName(STRPTR Source,STRPTR Buffer,LONG BufferSize):
  254.      *
  255.      *    Build a valid file and path name, including path
  256.      *    substitution, etc.
  257.      */
  258.  
  259. STATIC STRPTR
  260. NewFileName(STRPTR Source,STRPTR Buffer,LONG BufferSize)
  261. {
  262.     if(Config->TransferConfig->OverridePath && !Uploading)
  263.     {
  264.         if(DownloadPath)
  265.             LimitedStrcpy(BufferSize,Buffer,DownloadPath);
  266.         else
  267.             LimitedStrcpy(BufferSize,Buffer,Config->PathConfig->BinaryDownloadPath);
  268.  
  269.         if(!Buffer[0])
  270.         {
  271.             if(!LocalGetCurrentDirName(Buffer,BufferSize))
  272.                 Buffer[0] = 0;
  273.         }
  274.  
  275.         if(Buffer[0])
  276.         {
  277.             if(AddPart(Buffer,FilePart(Source),BufferSize))
  278.                 return(Buffer);
  279.         }
  280.     }
  281.     else
  282.     {
  283.         if(FilePart(Source) == Source)
  284.         {
  285.             if(LocalGetCurrentDirName(Buffer,BufferSize))
  286.             {
  287.                 if(AddPart(Buffer,Source,BufferSize))
  288.                     return(Buffer);
  289.             }
  290.         }
  291.     }
  292.  
  293.     LimitedStrcpy(BufferSize,Buffer,Source);
  294.  
  295.     return(Source);
  296. }
  297.  
  298.     /* IdentifySource(STRPTR Name,STRPTR BBSName,struct DateStamp *OpenDate):
  299.      *
  300.      *    Attach source information (BBS name, date and time) to a file.
  301.      */
  302.  
  303. STATIC VOID
  304. IdentifySource(STRPTR Name,STRPTR BBSName,struct DateStamp *OpenDate)
  305. {
  306.     UBYTE LocalBuffer[MAX_FILENAME_LENGTH],Time[40],Date[40];
  307.     struct DateTime    DateTime;
  308.  
  309.     CopyMem(OpenDate,&DateTime.dat_Stamp,sizeof(struct DateStamp));
  310.  
  311.         /* Prepare for date conversion. */
  312.  
  313.     DateTime.dat_Format        = FORMAT_DEF;
  314.     DateTime.dat_Flags        = 0;
  315.     DateTime.dat_StrDay        = NULL;
  316.     DateTime.dat_StrDate    = Date;
  317.     DateTime.dat_StrTime    = Time;
  318.  
  319.         /* Convert the date. */
  320.  
  321.     if(DateToStr(&DateTime))
  322.     {
  323.         StripSpaces(BBSName);
  324.         StripSpaces(Date);
  325.         StripSpaces(Time);
  326.  
  327.         if(BBSName[0])
  328.             LimitedSPrintf(sizeof(LocalBuffer),LocalBuffer,"%s %s %s",BBSName,Date,Time);
  329.         else
  330.             LimitedSPrintf(sizeof(LocalBuffer),LocalBuffer,"%s %s",Date,Time);
  331.  
  332.         SetComment(Name,LocalBuffer);
  333.     }
  334. }
  335.  
  336.     /* GetSeconds(STRPTR String):
  337.      *
  338.      *    Tries to turn a string of the format hh:mm:ss into
  339.      *    an integer number.
  340.      */
  341.  
  342. STATIC LONG
  343. GetSeconds(STRPTR String)
  344. {
  345.     UBYTE Buffer[20];
  346.     LONG Seconds;
  347.  
  348.     memset(Buffer,0,20);
  349.     Seconds = 0;
  350.  
  351.     strcpy(Buffer,String);
  352.  
  353.     Seconds += Atol(&Buffer[6]);
  354.  
  355.     Buffer[5] = 0;
  356.  
  357.     Seconds += Atol(&Buffer[3]) * 60;
  358.  
  359.     Buffer[2] = 0;
  360.  
  361.     Seconds += Atol(&Buffer[0]) * 3600;
  362.  
  363.     return(Seconds);
  364. }
  365.  
  366.     /* TruncateName(STRPTR FileName):
  367.      *
  368.      *    Truncates a file name to a maximum of 48 characters.
  369.      */
  370.  
  371. STATIC STRPTR
  372. TruncateName(STRPTR FileName)
  373. {
  374.     LONG Len;
  375.  
  376.     Len = strlen(FileName);
  377.  
  378.     if(Len > 48)
  379.     {
  380.         LONG i;
  381.  
  382.         for(i = Len - 48 ; i < Len ; i++)
  383.         {
  384.             if(i >= Len - 44 && FileName[i] == '/')
  385.             {
  386.                 STATIC UBYTE NameBuffer[MAX_FILENAME_LENGTH];
  387.  
  388.                 strcpy(NameBuffer,".../");
  389.  
  390.                 LimitedStrcat(sizeof(NameBuffer),NameBuffer,&FileName[i + 1]);
  391.  
  392.                 return(NameBuffer);
  393.             }
  394.         }
  395.     }
  396.  
  397.     return(FileName);
  398. }
  399.  
  400.     /* IsBlockMappedDevice(struct MsgPort *Handler):
  401.      *
  402.      *    See if the lock given points to a block mapped filing
  403.      *    system.
  404.      */
  405.  
  406. STATIC BOOL
  407. IsBlockMappedDevice(struct MsgPort *Handler)
  408. {
  409.     struct DosList *Entry;
  410.     BOOL IsBlockMapped;
  411.  
  412.     IsBlockMapped = FALSE;
  413.  
  414.         /* Find the device the lock belongs to */
  415.  
  416.     Entry = LockDosList(LDF_DEVICES | LDF_READ);
  417.  
  418.     while(Entry = NextDosEntry(Entry,LDF_DEVICES))
  419.     {
  420.         if(Entry->dol_Task == Handler)
  421.         {
  422.             struct FileSysStartupMsg *Startup = (struct FileSysStartupMsg *)BADDR(Entry->dol_misc.dol_handler.dol_Startup);
  423.  
  424.                 /* Plain filing system usually don't */
  425.                 /* use the startup entry */
  426.  
  427.             if(TypeOfMem(Startup))
  428.             {
  429.                 struct DosEnvec    *Environ;
  430.                 STRPTR Name;
  431.  
  432.                 Name    = (STRPTR)BADDR(Startup->fssm_Device);
  433.                 Environ    = (struct DosEnvec *)BADDR(Startup->fssm_Environ);
  434.  
  435.                     /* Valid data in the startup entry? */
  436.  
  437.                 if(TypeOfMem(Name) && TypeOfMem(Environ))
  438.                 {
  439.                     struct IOStdReq Request;
  440.  
  441.                     memset(&Request,0,sizeof(Request));
  442.                     Request.io_Message.mn_Length = sizeof(Request);
  443.  
  444.                         /* Last chance, try to open the device driver */
  445.  
  446.                     if(!OpenDevice(Name + 1,Startup->fssm_Unit,(struct IORequest *)&Request,Startup->fssm_Flags))
  447.                     {
  448.                             /* This is a block mapped filing system */
  449.  
  450.                         IsBlockMapped = TRUE;
  451.  
  452.                         CloseDevice((struct IORequest *)&Request);
  453.  
  454.                         break;
  455.                     }
  456.                 }
  457.             }
  458.         }
  459.     }
  460.  
  461.     UnLockDosList(LDF_DEVICES | LDF_READ);
  462.  
  463.     return(IsBlockMapped);
  464. }
  465.  
  466.     /* CalculateBlocks(LONG Size,LONG BlockSize):
  467.      *
  468.      *    Calculate the number of blocks a file will
  469.      *    occupy if saved to a disk.
  470.      */
  471.  
  472. STATIC LONG
  473. CalculateBlocks(LONG Size,LONG BlockSize)
  474. {
  475.     LONG Blocks,Extension;
  476.     BOOL HasExtension;
  477.  
  478.     Blocks            = 1;        /* One for the file header. */
  479.     Extension        = 0;        /* How many block pointers yet. */
  480.     HasExtension    = FALSE;    /* No extension block yet. */
  481.  
  482.         /* Round to next block. */
  483.  
  484.     Size = ((Size + BlockSize - 1) / BlockSize) * BlockSize;
  485.  
  486.     while(Size)
  487.     {
  488.             /* Add another block. */
  489.  
  490.         Blocks++;
  491.  
  492.             /* Subtract another block. */
  493.  
  494.         Size -= BlockSize;
  495.  
  496.             /* Add another block pointer, if 72 have been
  497.              * added, add another extension block.
  498.              */
  499.  
  500.         if((Extension++) == 72)
  501.         {
  502.                 /* If no extension block has been generated
  503.                  * yet, we were running on the block pointers
  504.                  * of the file header itself.
  505.                  */
  506.  
  507.             if(!HasExtension)
  508.                 HasExtension = TRUE;
  509.             else
  510.                 Blocks++;
  511.  
  512.                 /* Reset extension block counter. */
  513.  
  514.             Extension = 0;
  515.         }
  516.     }
  517.  
  518.     return(Blocks);
  519. }
  520.  
  521.     /* SerialErrorReport(struct IOExtSer *Request):
  522.      *
  523.      *    Report a certain I/O error cause.
  524.      */
  525.  
  526. STATIC BYTE
  527. SerialErrorReport(struct IOExtSer *Request)
  528. {
  529.     STRPTR ErrorMessage;
  530.     BOOL IsFatal;
  531.  
  532.     switch(Request->IOSer.io_Error)
  533.     {
  534.         case SerErr_LineErr:
  535.  
  536.             ErrorMessage = LocaleString(MSG_TERMXPR_ERROR_HARDWARE_DATA_OVERRUN_TXT);
  537.  
  538.             IsFatal = FALSE;
  539.  
  540.             break;
  541.  
  542.         case SerErr_ParityErr:
  543.  
  544.             ErrorMessage = LocaleString(MSG_TERMXPR_ERROR_PARITY_ERROR_TXT);
  545.  
  546.             IsFatal = TRUE;
  547.  
  548.             break;
  549.  
  550.         case SerErr_TimerErr:
  551.  
  552.             ErrorMessage = LocaleString(MSG_TERMXPR_ERROR_TIMER_ERROR_TXT);
  553.  
  554.             IsFatal = FALSE;
  555.  
  556.             break;
  557.  
  558.         case SerErr_BufOverflow:
  559.  
  560.             ErrorMessage = LocaleString(MSG_TERMXPR_ERROR_READ_BUFFER_OVERFLOWED_TXT);
  561.  
  562.             IsFatal = FALSE;
  563.  
  564.             break;
  565.  
  566.         case SerErr_NoDSR:
  567.  
  568.             ErrorMessage = LocaleString(MSG_TERMXPR_ERROR_NO_DSR_TXT);
  569.  
  570.             IsFatal = TRUE;
  571.  
  572.             break;
  573.  
  574.         case SerErr_DetectedBreak:
  575.  
  576.             ErrorMessage = LocaleString(MSG_TERMXPR_ERROR_BREAK_DETECTED_TXT);
  577.  
  578.             IsFatal = TRUE;
  579.  
  580.             break;
  581.  
  582.         default:
  583.  
  584.             ErrorMessage = LocaleString(MSG_TERMXPR_ERROR_UNKNOWN_CAUSE_TXT);
  585.  
  586.             IsFatal = FALSE;
  587.  
  588.             break;
  589.     }
  590.  
  591.     AddTransferInfo(TRUE,LocaleString(MSG_TERMXPR_ERROR_TEMPLATE_TXT),Request->IOSer.io_Error,ErrorMessage);
  592.  
  593.     if(IsFatal)
  594.     {
  595.         TransferError = TRUE;
  596.  
  597.         return(TRUE);
  598.     }
  599.     else
  600.         return(FALSE);
  601. }
  602.  
  603.     /* xpr_fopen(STRPTR FileName,STRPTR AccessMode):
  604.      *
  605.      *    Open a file for random access.
  606.      */
  607.  
  608. LONG SAVE_DS ASM
  609. xpr_fopen(REG(a0) STRPTR FileName,REG(a1) STRPTR AccessMode)
  610. {
  611.     UBYTE RealName[MAX_FILENAME_LENGTH];
  612.     struct Buffer *File;
  613.  
  614.     DB(kprintf("xpr_fopen(\"%s\",\"%s\")\n",FileName,AccessMode));
  615.  
  616.     UserWasNotified                = FALSE;
  617.     CarrierLossWasDisplayed        = FALSE;
  618.     ErrorMessageWasDisplayed    = FALSE;
  619.     FileSysTypeWasChecked        = FALSE;
  620.     CheckSpace                    = TRUE;
  621.     DidTransfer                    = TRUE;
  622.  
  623.         /* Reset transfer counters. */
  624.  
  625.     ByteVal            = 0;
  626.     ByteMax            = 0;
  627.     TimeVal            = 0;
  628.     TimeMax            = 0;
  629.  
  630.     LastTimeDelta    = 0;
  631.     LastPercent        = -1;
  632.  
  633.         /* Reset CPS statistics. */
  634.  
  635.     CPS_Minimum        = (ULONG)~0;
  636.     CPS_Maximum        = 0;
  637.     CPS_Average        = 0;
  638.     CPS_Current        = 0;
  639.     CPS_Count        = 0;
  640.     CPS_Last        = 0;
  641.  
  642.         /* No window available for output? */
  643.  
  644.     if(!TransferWindow)
  645.         OpenTransferWindow();
  646.  
  647.     if(OriginalName[0])
  648.     {
  649.         if(!Stricmp(ShrunkenName,FileName))
  650.             FileName = OriginalName;
  651.     }
  652.  
  653.         /* Determine the file name. */
  654.  
  655.     FileName = NewFileName(FileName,RealName,sizeof(RealName));
  656.  
  657.         /* Determine file transfer mode... */
  658.  
  659.     if(File = BufferOpen(FileName,AccessMode))
  660.     {
  661.         switch(AccessMode[0])
  662.         {
  663.             case 'r':
  664.  
  665.                 LogAction(LocaleString(MSG_TERMXPR_LOGMSG_SEND_FILE_TXT),FileName);
  666.                 break;
  667.  
  668.             case 'w':
  669.  
  670.                 LogAction(LocaleString(MSG_TERMXPR_LOGMSG_RECEIVE_FILE_TXT),FileName);
  671.                 break;
  672.  
  673.             case 'a':
  674.  
  675.                 LogAction(LocaleString(MSG_TERMXPR_LOGMSG_UPDATE_FILE_TXT),FileName);
  676.                 break;
  677.         }
  678.  
  679.         CurrentFile = File;
  680.     }
  681.  
  682.     return((LONG)File);
  683. }
  684.  
  685.     /* xpr_fclose(struct Buffer *File):
  686.      *
  687.      *    Close a file opened by xpr_fopen.
  688.      */
  689.  
  690. LONG SAVE_DS ASM
  691. xpr_fclose(REG(a0) struct Buffer *File)
  692. {
  693.     UBYTE RealName[MAX_FILENAME_LENGTH];
  694.     struct DateStamp OpenDate;
  695.     BOOL WriteAccess,Used;
  696.  
  697.     DB(kprintf("xpr_fclose(0x%08lx)\n",File));
  698.  
  699.         /* This happened only once so far, but... */
  700.  
  701.     if(!File)
  702.     {
  703.         CurrentFile = NULL;
  704.  
  705.         return(1);
  706.     }
  707.  
  708.         /* Save some information. */
  709.  
  710.     OpenDate    = File->OpenDate;
  711.     WriteAccess    = File->WriteAccess;
  712.     Used        = File->Used;
  713.  
  714.         /* Get the name of the file. */
  715.  
  716.     if(!NameFromFH(File->FileHandle,RealName,sizeof(RealName)))
  717.         RealName[0] = 0;
  718.  
  719.         /* Close the file and see what it brings... */
  720.  
  721.     if(BufferClose(File) && RealName[0])
  722.     {
  723.             /* Did any access take place at all?
  724.              * xprzmodem.library for example just
  725.              * opens and closes a file in order to
  726.              * see if it exists.
  727.              */
  728.  
  729.         if(!Used)
  730.             LogAction(LocaleString(MSG_TERMXPR_CLOSE_FILE_TXT),RealName);
  731.         else
  732.         {
  733.                 /* Did we receive or send a file? */
  734.  
  735.             if(WriteAccess)
  736.             {
  737.                 LONG Size;
  738.  
  739.                     /* Did the file remain empty? */
  740.  
  741.                 if(!(Size = GetFileSize(RealName)))
  742.                 {
  743.                     AddTransferInfo(TRUE,LocaleString(MSG_TERMXPR_FILE_REMOVED_TXT),FilePart(RealName));
  744.  
  745.                         /* Delete empty file. */
  746.  
  747.                     if(DeleteFile(RealName))
  748.                         LogAction(LocaleString(MSG_TERMXPR_CLOSE_FILE_REMOVED_TXT),RealName);
  749.                     else
  750.                         LogAction(LocaleString(MSG_TERMXPR_CLOSE_FILE_TXT),RealName);
  751.                 }
  752.                 else
  753.                 {
  754.                     struct Node *SomeNode;
  755.  
  756.                     if(ByteMax)
  757.                     {
  758.                         if(Size >= ByteMax)
  759.                             AddTransferInfo(FALSE,LocaleString(MSG_TERMXPR_FILE_RECEIVED_TXT),FilePart(RealName));
  760.                         else
  761.                             AddTransferInfo(TRUE,LocaleString(MSG_TERMXPR_INCOMPLETE_FILE_RECEIVED_TXT),FilePart(RealName));
  762.                     }
  763.  
  764.                         /* Try to identify the file type. */
  765.  
  766.                     if(Config->TransferConfig->IdentifyCommand[0])
  767.                     {
  768.                         UBYTE LocalBuffer[2 * MAX_FILENAME_LENGTH];
  769.                         LONG i;
  770.                         BOOL GotIt;
  771.  
  772.                         GotIt = FALSE;
  773.  
  774.                         for(i = 0 ; i < strlen(Config->TransferConfig->IdentifyCommand) - 1 ; i++)
  775.                         {
  776.                             if(Config->TransferConfig->IdentifyCommand[i] == '%' && Config->TransferConfig->IdentifyCommand[i+1] == 's')
  777.                             {
  778.                                 GotIt = TRUE;
  779.                                 break;
  780.                             }
  781.                         }
  782.  
  783.                         if(GotIt)
  784.                             LimitedSPrintf(sizeof(LocalBuffer),LocalBuffer,Config->TransferConfig->IdentifyCommand,RealName);
  785.                         else
  786.                         {
  787.                             LimitedStrcpy(sizeof(LocalBuffer),LocalBuffer,Config->TransferConfig->IdentifyCommand);
  788.                             LimitedStrcat(sizeof(LocalBuffer),LocalBuffer," \"");
  789.                             LimitedStrcat(sizeof(LocalBuffer),LocalBuffer,RealName);
  790.                             LimitedStrcat(sizeof(LocalBuffer),LocalBuffer,"\"");
  791.                         }
  792.  
  793.                         LaunchCommandAsync(LocalBuffer);
  794.                     }
  795.                     else
  796.                     {
  797.                         switch(Config->TransferConfig->IdentifyFiles)
  798.                         {
  799.                             case IDENTIFY_IGNORE:
  800.  
  801.                                 if(Config->MiscConfig->CreateIcons)
  802.                                     Identify(RealName,FALSE);
  803.  
  804.                                 break;
  805.  
  806.                             case IDENTIFY_FILETYPE:
  807.  
  808.                                 Identify(RealName,TRUE);
  809.                                 break;
  810.  
  811.                             case IDENTIFY_SOURCE:
  812.  
  813.                                 IdentifySource(RealName,CurrentBBSName,&OpenDate);
  814.  
  815.                                 if(Config->MiscConfig->CreateIcons)
  816.                                     Identify(RealName,FALSE);
  817.  
  818.                                 break;
  819.                         }
  820.                     }
  821.  
  822.                     if(CPS_Minimum == (ULONG)~0)
  823.                         CPS_Minimum = 0;
  824.  
  825.                     if(CPS_Count == 0)
  826.                     {
  827.                         CPS_Average    = 0;
  828.                         CPS_Count    = 1;
  829.                     }
  830.  
  831.                     LogAction(LocaleString(MSG_TERMXPR_CLOSE_FILE_BYTES_TXT),RealName,Size,CPS_Minimum,CPS_Average / CPS_Count,CPS_Maximum);
  832.  
  833.                     if(SomeNode = CreateGenericListNode(0,RealName))
  834.                         AddGenericListNode(GenericListTable[GLIST_DOWNLOAD],SomeNode,ADD_GLIST_BOTTOM,FALSE);
  835.                 }
  836.             }
  837.             else
  838.             {
  839.                     /* Set the archived bit on files we uploaded? */
  840.  
  841.                 if(Config->TransferConfig->SetArchivedBit)
  842.                     AddProtection(RealName,FIBF_ARCHIVE);
  843.  
  844.                 AddTransferInfo(FALSE,LocaleString(MSG_TERMXPR_FILE_SENT_TXT),FilePart(RealName));
  845.  
  846.                 LogAction(LocaleString(MSG_TERMXPR_CLOSE_FILE_TXT),RealName);
  847.  
  848.                 RemoveUploadListItem(RealName);
  849.             }
  850.         }
  851.     }
  852.  
  853.     CurrentFile = NULL;
  854.  
  855.     return(1);
  856. }
  857.  
  858.     /* xpr_fread(APTR Buffer,LONG Size,LONG Count,struct Buffer *File):
  859.      *
  860.      *    Read a few bytes from a file.
  861.      */
  862.  
  863. LONG SAVE_DS ASM
  864. xpr_fread(REG(a0) APTR Buffer,REG(d0) LONG Size,REG(d1) LONG Count,REG(a1) struct Buffer *File)
  865. {
  866.     DB(kprintf("xpr_fread(0x%08lx,%ld,%ld,0x%08lx)\n",Buffer,Size,Count,File));
  867.  
  868.     return(BufferRead(File,Buffer,Size * Count) / Size);
  869. }
  870.  
  871.     /* xpr_fwrite(APTR Buffer,LONG Size,LONG Count,struct Buffer *File):
  872.      *
  873.      *    Write a few bytes to a file.
  874.      */
  875.  
  876. LONG SAVE_DS ASM
  877. xpr_fwrite(REG(a0) APTR Buffer,REG(d0) LONG Size,REG(d1) LONG Count,REG(a1) struct Buffer *File)
  878. {
  879.     DB(kprintf("xpr_fwrite(0x%08lx,%ld,%ld,0x%08lx)\n",Buffer,Size,Count,File));
  880.  
  881.     return(BufferWrite(File,Buffer,Size * Count) / Size);
  882. }
  883.  
  884.     /* xpr_fseek(struct Buffer *File,LONG Offset,LONG Origin):
  885.      *
  886.      *    Move the read/write pointer in a file.
  887.      */
  888.  
  889. LONG SAVE_DS ASM
  890. xpr_fseek(REG(a0) struct Buffer *File,REG(d0) LONG Offset,REG(d1) LONG Origin)
  891. {
  892.     DB(kprintf("xpr_fseek(0x%08lx,%ld,%ld)\n",File,Offset,Origin));
  893.  
  894.     return(BufferSeek(File,Offset,Origin) ? 0 : -1);
  895. }
  896.  
  897.     /* xpr_sread(APTR Buffer,LONG Size,LONG Timeout):
  898.      *
  899.      *    Read a few bytes from the serial port (including
  900.      *    timeouts).
  901.      */
  902.  
  903. LONG SAVE_DS ASM
  904. xpr_sread(REG(a0) APTR Buffer,REG(d0) ULONG Size,REG(d1) ULONG Timeout)
  905. {
  906.     DB(kprintf("xpr_sread(0x%08lx,%ld,%ld)\n",Buffer,Size,Timeout));
  907.  
  908.         /* No window available for output? */
  909.  
  910.     if(!TransferWindow)
  911.     {
  912.         if(!OpenTransferWindow())
  913.             return(-1);
  914.     }
  915.  
  916.         /* Are both IORequests available? */
  917.  
  918.     if(WriteRequest && ReadRequest)
  919.     {
  920.             /* Valid size parameter? */
  921.  
  922.         if(Size > 0)
  923.         {
  924.             ULONG SignalSet,SerialMask,WindowMask;
  925.             ULONG Waiting;
  926.             UWORD Status;
  927.  
  928.             GetSerialInfo(&Waiting,&Status);
  929.  
  930.                 /* Return error if carrier is lost. */
  931.  
  932.             if(CheckForCarrierLoss(Status))
  933.                 return(-1);
  934.  
  935.                 /* Is there data waiting to be read? */
  936.  
  937.             if(Waiting > 0)
  938.             {
  939.                     /* No timeout specified? Read as many
  940.                      * bytes as available.
  941.                      */
  942.  
  943.                 if(Timeout == 0)
  944.                 {
  945.                     if(Waiting > Size)
  946.                         Waiting = Size;
  947.  
  948.                     if(DoSerialRead(Buffer,Waiting))
  949.                     {
  950.                         if(SerialErrorReport(ReadRequest))
  951.                             return(-1);
  952.                         else
  953.                             Waiting = ReadRequest->IOSer.io_Actual;
  954.                     }
  955.  
  956.                     BytesIn += Waiting;
  957.  
  958.                     return((LONG)Waiting);
  959.                 }
  960.  
  961.                     /* Enough data pending to be read? */
  962.  
  963.                 if(Waiting >= Size)
  964.                 {
  965.                     if(DoSerialRead(Buffer,Size))
  966.                     {
  967.                         if(SerialErrorReport(ReadRequest))
  968.                             return(-1);
  969.                         else
  970.                             Size = ReadRequest->IOSer.io_Actual;
  971.                     }
  972.  
  973.                     BytesIn += Size;
  974.  
  975.                     return((LONG)Size);
  976.                 }
  977.             }
  978.             else
  979.             {
  980.                     /* No timeout and no data available:
  981.                      * return immediately.
  982.                      */
  983.  
  984.                 if(Timeout == 0)
  985.                     return(0);
  986.             }
  987.  
  988.                 /* Set up the timer. */
  989.  
  990.             StartTime(Timeout / MILLION,Timeout % MILLION);
  991.  
  992.                 /* Set up the read request. */
  993.  
  994.             StartSerialRead(Buffer,Size);
  995.  
  996.                 /* We'll need them in a minute */
  997.  
  998.             SerialMask = PORTMASK(ReadPort);
  999.             WindowMask = PORTMASK(TransferWindow->UserPort);
  1000.  
  1001.             while(TRUE)
  1002.             {
  1003.                 SignalSet = Wait(SerialMask | SIG_TIMER | WindowMask);
  1004.  
  1005.                     /* Abort the file transfer? */
  1006.  
  1007.                 if(SignalSet & WindowMask)
  1008.                 {
  1009.                     LONG Result = CheckAbort(FALSE);
  1010.  
  1011.                         /* Let's get out of here
  1012.                          * and hope that the protocol
  1013.                          * will call xpr_chkabort()
  1014.                          * in time.
  1015.                          */
  1016.  
  1017.                     if(Result != 0)
  1018.                     {
  1019.                         TransferAbortState = Result;
  1020.  
  1021.                             /* Stop whatever we were doing. */
  1022.  
  1023.                         StopSerialRead();
  1024.                         StopTime();
  1025.  
  1026.                             /* Does the user want to cancel
  1027.                              * the transmission?
  1028.                              */
  1029.  
  1030.                         if(Result < 0)
  1031.                         {
  1032.                                 /* Do it the hard way */
  1033.  
  1034.                             if(TransferAbortCount++)
  1035.                                 CancelZModem();
  1036.  
  1037.                             return(Result);
  1038.                         }
  1039.                         else
  1040.                         {
  1041.                                 /* So it's something else. Pick up whatever
  1042.                                  * we received and return immediately.
  1043.                                  */
  1044.  
  1045.                             if(ReadRequest->IOSer.io_Actual > 0)
  1046.                             {
  1047.                                 BytesIn += ReadRequest->IOSer.io_Actual;
  1048.  
  1049.                                 return((LONG)ReadRequest->IOSer.io_Actual);
  1050.                             }
  1051.                             else
  1052.                             {
  1053.                                     /* Take a second look and query the number of
  1054.                                      * bytes ready to be received, there may
  1055.                                      * still be some bytes in the buffer.
  1056.                                      * Note: this depends on the way the
  1057.                                      * driver handles read abort.
  1058.                                      */
  1059.  
  1060.                                 GetSerialInfo(&Waiting,&Status);
  1061.  
  1062.                                     /* Don't read too much. */
  1063.  
  1064.                                 if(Size > Waiting)
  1065.                                     Size = Waiting;
  1066.  
  1067.                                     /* Are there any bytes to be transferred? */
  1068.  
  1069.                                 if(Size > 0)
  1070.                                 {
  1071.                                         /* Read the data. */
  1072.  
  1073.                                     if(DoSerialRead(Buffer,Size))
  1074.                                     {
  1075.                                         if(SerialErrorReport(ReadRequest))
  1076.                                             return(-1);
  1077.                                         else
  1078.                                             Size = ReadRequest->IOSer.io_Actual;
  1079.                                     }
  1080.  
  1081.                                     BytesIn += Size;
  1082.                                 }
  1083.                                 else
  1084.                                 {
  1085.                                         /* Ok, so there is no data in the buffer. */
  1086.                                         /* Check if the carrier signal is still */
  1087.                                         /* present */
  1088.  
  1089.                                     if(CheckForCarrierLoss(Status))
  1090.                                         return(-1);
  1091.                                 }
  1092.  
  1093.                                 return((LONG)Size);
  1094.                             }
  1095.                         }
  1096.                     }
  1097.                 }
  1098.  
  1099.                     /* Receive buffer filled? */
  1100.  
  1101.                 if(SignalSet & SerialMask)
  1102.                 {
  1103.                         /* Abort the timer request. */
  1104.  
  1105.                     StopTime();
  1106.  
  1107.                         /* Did the request terminate gracefully? */
  1108.  
  1109.                     if(WaitSerialRead())
  1110.                     {
  1111.                         if(SerialErrorReport(ReadRequest))
  1112.                             return(-1);
  1113.                         else
  1114.                             Size = ReadRequest->IOSer.io_Actual;
  1115.                     }
  1116.  
  1117.                     BytesIn += Size;
  1118.  
  1119.                     return((LONG)Size);
  1120.                 }
  1121.  
  1122.                     /* Hit by timeout? */
  1123.  
  1124.                 if(SignalSet & SIG_TIMER)
  1125.                 {
  1126.                         /* Abort the read request. */
  1127.  
  1128.                     StopSerialRead();
  1129.  
  1130.                         /* Remove the timer request. */
  1131.  
  1132.                     WaitTime();
  1133.  
  1134.                         /* Did the driver receive any
  1135.                          * data?
  1136.                          */
  1137.  
  1138.                     if(ReadRequest->IOSer.io_Actual > 0)
  1139.                     {
  1140.                         BytesIn += ReadRequest->IOSer.io_Actual;
  1141.  
  1142.                         return((LONG)ReadRequest->IOSer.io_Actual);
  1143.                     }
  1144.                     else
  1145.                     {
  1146.                             /* Take a second look and query the number of
  1147.                              * bytes ready to be received, there may
  1148.                              * still be some bytes in the buffer.
  1149.                              * Note: this depends on the way the
  1150.                              * driver handles read abort.
  1151.                              */
  1152.  
  1153.                         GetSerialInfo(&Waiting,&Status);
  1154.  
  1155.                             /* Don't read too much. */
  1156.  
  1157.                         if(Size > Waiting)
  1158.                             Size = Waiting;
  1159.  
  1160.                             /* Are there any bytes to be transferred? */
  1161.  
  1162.                         if(Size > 0)
  1163.                         {
  1164.                                 /* Read the data. */
  1165.  
  1166.                             if(DoSerialRead(Buffer,Size))
  1167.                             {
  1168.                                 if(SerialErrorReport(ReadRequest))
  1169.                                     return(-1);
  1170.                                 else
  1171.                                     Size = ReadRequest->IOSer.io_Actual;
  1172.                             }
  1173.  
  1174.                             BytesIn += Size;
  1175.                         }
  1176.                         else
  1177.                         {
  1178.                                 /* Ok, so there is no data in the buffer. */
  1179.                                 /* Check if the carrier signal is still */
  1180.                                 /* present */
  1181.  
  1182.                             if(CheckForCarrierLoss(Status))
  1183.                                 return(-1);
  1184.                         }
  1185.  
  1186.                         return((LONG)Size);
  1187.                     }
  1188.                 }
  1189.             }
  1190.         }
  1191.         else
  1192.             return(0);
  1193.     }
  1194.     else
  1195.         return(-1);
  1196. }
  1197.  
  1198.     /* xpr_swrite(APTR Buffer,LONG Size):
  1199.      *
  1200.      *    Write a few bytes to the serial port.
  1201.      */
  1202.  
  1203. LONG SAVE_DS ASM
  1204. xpr_swrite(REG(a0) APTR Buffer,REG(d0) LONG Size)
  1205. {
  1206.     DB(kprintf("xpr_swrite(0x%08lx,%ld)\n",Buffer,Size));
  1207.  
  1208.         /* Return error if carrier is lost. */
  1209.  
  1210.     if(WriteRequest && !CheckForCarrierLoss(GetSerialStatus()))
  1211.     {
  1212.             /* Send the data. */
  1213.  
  1214.         if(!DoSerialWrite(Buffer,Size))
  1215.             BytesOut += Size;
  1216.         else
  1217.         {
  1218.             if(SerialErrorReport(WriteRequest))
  1219.                 return(-1);
  1220.         }
  1221.  
  1222.         return(0);
  1223.     }
  1224.  
  1225.     return(-1);
  1226. }
  1227.  
  1228.     /* xpr_sflush():
  1229.      *
  1230.      *    Release the contents of all serial buffers.
  1231.      */
  1232.  
  1233. LONG SAVE_DS
  1234. xpr_sflush()
  1235. {
  1236.     DB(kprintf("xpr_sflush()\n"));
  1237.  
  1238.     return((LONG)FlushSerialRead());
  1239. }
  1240.  
  1241.     /* xpr_update(struct XPR_UPDATE *UpdateInfo):
  1242.      *
  1243.      *    Update the information displayed in the transfer window.
  1244.      */
  1245.  
  1246. LONG SAVE_DS ASM
  1247. xpr_update(REG(a0) struct XPR_UPDATE *UpdateInfo)
  1248. {
  1249.     STATIC UBYTE RealName[MAX_FILENAME_LENGTH];
  1250.  
  1251.     DB(kprintf("xpr_update(0x%08lx)\n",UpdateInfo));
  1252.  
  1253.         /* No window available for output? */
  1254.  
  1255.     if(!TransferWindow)
  1256.     {
  1257.         if(!OpenTransferWindow())
  1258.             return(0);
  1259.     }
  1260.  
  1261.     if(UpdateInfo)
  1262.     {
  1263.         BOOL NewByte,NewTime;
  1264.  
  1265.         NewByte    = FALSE;
  1266.         NewTime = FALSE;
  1267.  
  1268.         if(TransferWindow->Flags & WFLG_ZOOMED)
  1269.         {
  1270.             STATIC ULONG LastSeconds;
  1271.  
  1272.             struct timeval Now;
  1273.             BOOL Refresh;
  1274.  
  1275.             GetSysTime(&Now);
  1276.  
  1277.             Refresh = TRUE;
  1278.  
  1279.             if(!TransferZoomed)
  1280.             {
  1281.                 LastSeconds = Now.tv_secs;
  1282.  
  1283.                 TransferZoomed = TRUE;
  1284.             }
  1285.             else
  1286.             {
  1287.                 if(Now.tv_secs > LastSeconds)
  1288.                     LastSeconds = Now.tv_secs;
  1289.                 else
  1290.                     Refresh = FALSE;
  1291.             }
  1292.  
  1293.             if(Refresh)
  1294.             {
  1295.                 STRPTR Template;
  1296.  
  1297.                 if(LastPercent > 0)
  1298.                     Template = "%s [%s %ld%%]";
  1299.                 else
  1300.                     Template = "%s [%s]";
  1301.  
  1302.                 LimitedSPrintf(sizeof(TransferTitleBuffer),TransferTitleBuffer,Template,TransferWindowTitle,TruncateName(RealName),LastPercent);
  1303.  
  1304.                 SetWindowTitles(TransferWindow,TransferTitleBuffer,(STRPTR)-1);
  1305.             }
  1306.         }
  1307.         else
  1308.         {
  1309.             if(TransferZoomed)
  1310.             {
  1311.                 SetWindowTitles(TransferWindow,TransferWindowTitle,(STRPTR)-1);
  1312.  
  1313.                 TransferZoomed = FALSE;
  1314.             }
  1315.         }
  1316.  
  1317.         if((UpdateInfo->xpru_updatemask & XPRU_PROTOCOL) && UpdateInfo->xpru_protocol)
  1318.             LT_SetAttributes(TransferHandle,GAD_TRANSFER_PROTOCOL,GTTX_Text,UpdateInfo->xpru_protocol,TAG_DONE);
  1319.  
  1320.         if((UpdateInfo->xpru_updatemask & XPRU_MSG) && UpdateInfo->xpru_msg)
  1321.             AddTransferInfo(FALSE,UpdateInfo->xpru_msg);
  1322.  
  1323.         if((UpdateInfo->xpru_updatemask & XPRU_ERRORMSG) && UpdateInfo->xpru_errormsg)
  1324.             AddTransferInfo(TRUE,UpdateInfo->xpru_errormsg);
  1325.  
  1326.         if((UpdateInfo->xpru_updatemask & XPRU_FILENAME) && UpdateInfo->xpru_filename)
  1327.         {
  1328.             STRPTR FileName = UpdateInfo->xpru_filename;
  1329.  
  1330.             if(OriginalName[0])
  1331.             {
  1332.                 if(!Stricmp(ShrunkenName,FileName))
  1333.                     FileName = OriginalName;
  1334.             }
  1335.  
  1336.             FileName = NewFileName(FileName,RealName,sizeof(RealName));
  1337.  
  1338.             LT_SetAttributes(TransferHandle,GAD_TRANSFER_FILE,LABX_Index,0,LABX_Text,TruncateName(FileName),TAG_DONE);
  1339.  
  1340.             if(FileTransferInfo)
  1341.             {
  1342.                 LONG Percentage;
  1343.  
  1344.                 if(FileTransferInfo->CurrentFile)
  1345.                 {
  1346.                     struct FileTransferNode *Node;
  1347.                     STRPTR Name;
  1348.  
  1349.                     Node = (struct FileTransferNode *)FileTransferInfo->CurrentFile->MinNode.mln_Succ;
  1350.  
  1351.                     if(Node->MinNode.mln_Succ)
  1352.                         Name = TruncateName(Node->Name);
  1353.                     else
  1354.                         Name = "-";
  1355.  
  1356.                     LT_SetAttributes(TransferHandle,GAD_TRANSFER_FILE,LABX_Index,1,LABX_Text,Name,TAG_DONE);
  1357.                 }
  1358.  
  1359.                 PrintBox(GAD_TRANSFER_SIZE,2,ConvNumber,FileTransferInfo->TotalSize);
  1360.  
  1361.                 if(FileTransferInfo->TotalFiles)
  1362.                     Percentage = (100 * FileTransferInfo->DoneFiles) / FileTransferInfo->TotalFiles;
  1363.                 else
  1364.                     Percentage = 0;
  1365.  
  1366.                 PrintBox(GAD_TRANSFER_SIZE,4,LocaleString(MSG_TRANSFERPANEL_NUMBER_TO_GO_PERCENT_TXT),FileTransferInfo->DoneFiles,FileTransferInfo->TotalFiles,Percentage);
  1367.             }
  1368.         }
  1369.  
  1370.         if((UpdateInfo->xpru_updatemask & XPRU_FILESIZE) && UpdateInfo->xpru_filesize != -1)
  1371.         {
  1372.             STRPTR FormatSpecifier;
  1373.  
  1374.             if(CurrentFile && CurrentFile->InfoPort)
  1375.                 FormatSpecifier = ConvNumber10;
  1376.             else
  1377.                 FormatSpecifier = ConvNumber;
  1378.  
  1379.             PrintBox(GAD_TRANSFER_SIZE,0,FormatSpecifier,UpdateInfo->xpru_filesize);
  1380.  
  1381.             if(ByteMax = UpdateInfo->xpru_filesize)
  1382.                 NewByte = TRUE;
  1383.         }
  1384.  
  1385.         if((UpdateInfo->xpru_updatemask & XPRU_BYTES) && UpdateInfo->xpru_bytes != -1)
  1386.         {
  1387.             STRPTR FormatSpecifier;
  1388.  
  1389.             if(CurrentFile && CurrentFile->InfoPort)
  1390.                 FormatSpecifier = ConvNumber10;
  1391.             else
  1392.                 FormatSpecifier = ConvNumber;
  1393.  
  1394.             PrintBox(GAD_TRANSFER_SIZE,1,FormatSpecifier,ByteVal = UpdateInfo->xpru_bytes);
  1395.  
  1396.             if(FileTransferInfo)
  1397.             {
  1398.                 LONG Percentage;
  1399.                 ULONG Len;
  1400.  
  1401.                 Len = FileTransferInfo->DoneSize + UpdateInfo->xpru_bytes;
  1402.  
  1403.                 if(FileTransferInfo->TotalSize)
  1404.                     Percentage = (100 * Len) / FileTransferInfo->TotalSize;
  1405.                 else
  1406.                     Percentage = 0;
  1407.  
  1408.                 PrintBox(GAD_TRANSFER_SIZE,3,LocaleString(MSG_TRANSFERPANEL_NUMBER_PERCENT_TXT),Len,Percentage);
  1409.             }
  1410.  
  1411.             if(ByteMax)
  1412.                 NewByte = TRUE;
  1413.  
  1414.             if(CurrentFile)
  1415.             {
  1416.                 if(CurrentFile->InfoPort && !Uploading)
  1417.                 {
  1418.                     if(CurrentFile->InfoData.id_NumBlocks && CurrentFile->InfoData.id_BytesPerBlock)
  1419.                     {
  1420.                         LONG DisplaySpace,DisplayPercent;
  1421.                         STRPTR DisplayMessage;
  1422.  
  1423.                         DisplayMessage    = "";
  1424.                         DisplayPercent    = (100 * CurrentFile->InfoData.id_NumBlocksUsed) / CurrentFile->InfoData.id_NumBlocks;
  1425.                         DisplaySpace    = CurrentFile->InfoData.id_NumBlocks - CurrentFile->InfoData.id_NumBlocksUsed;
  1426.  
  1427.                         if(ByteMax && (CheckSpace || !FileSysTypeWasChecked))
  1428.                         {
  1429.                                 /* Did we take a look at the filing system yet? */
  1430.  
  1431.                             if(!FileSysTypeWasChecked)
  1432.                             {
  1433.                                 FileSysTypeWasChecked = TRUE;
  1434.  
  1435.                                     /* If this is not a block mapped device */
  1436.                                     /* we cannot guess what will happen if */
  1437.                                     /* it fills up (and whether it fills up at all). */
  1438.  
  1439.                                 if(!IsBlockMappedDevice(CurrentFile->InfoPort))
  1440.                                     CheckSpace = FALSE;
  1441.                             }
  1442.  
  1443.                                 /* Should we check the space left on the destination device? */
  1444.  
  1445.                             if(CheckSpace)
  1446.                             {
  1447.                                     /* Important! Only use the number of bytes still */
  1448.                                     /* to be transferred. */
  1449.  
  1450.                                 LONG Blocks = CalculateBlocks(ByteMax - ByteVal,CurrentFile->InfoData.id_BytesPerBlock);
  1451.  
  1452.                                     /* Is there less space left than we need? */
  1453.  
  1454.                                 if(DisplaySpace < Blocks)
  1455.                                 {
  1456.                                     if(!UserWasNotified)
  1457.                                     {
  1458.                                         UserWasNotified = TRUE;
  1459.  
  1460.                                         Say(LocaleString(MSG_TERMXPR_SAY_FILE_MAY_NOT_FIT_TXT));
  1461.                                     }
  1462.  
  1463.                                     DisplayMessage = LocaleString(MSG_TERMXPR_FILE_MAY_NOT_FIT_TXT);
  1464.                                 }
  1465.                             }
  1466.                         }
  1467.  
  1468.                         PrintBox(GAD_TRANSFER_FILE,2,LocaleString(MSG_TERMXPR_BYTES_FULL_TXT),DisplaySpace * CurrentFile->InfoData.id_BytesPerBlock,DisplayPercent,DisplayMessage);
  1469.                     }
  1470.                 }
  1471.             }
  1472.         }
  1473.  
  1474.         if((UpdateInfo->xpru_updatemask & XPRU_BLOCKS) && UpdateInfo->xpru_blocks != -1)
  1475.             PrintBox(GAD_TRANSFER_SIZE,5,ConvNumber,UpdateInfo->xpru_blocks);
  1476.  
  1477.         if((UpdateInfo->xpru_updatemask & XPRU_DATARATE) && UpdateInfo->xpru_datarate > 0)
  1478.         {
  1479.             LONG Change;
  1480.  
  1481.             CPS_Current = UpdateInfo->xpru_datarate;
  1482.  
  1483.             if(CPS_Last)
  1484.             {
  1485.                 if(CPS_Last < UpdateInfo->xpru_datarate)
  1486.                     Change = (100 * (UpdateInfo->xpru_datarate - CPS_Last)) / CPS_Last;
  1487.                 else
  1488.                     Change = (100 * (CPS_Last - UpdateInfo->xpru_datarate)) / CPS_Last;
  1489.             }
  1490.             else
  1491.                 Change = 100;
  1492.  
  1493.             if(Change > 1)
  1494.             {
  1495.                 PrintBox(GAD_TRANSFER_SECONDS,0,ConvNumber,UpdateInfo->xpru_datarate);
  1496.  
  1497.                 CPS_Last = UpdateInfo->xpru_datarate;
  1498.             }
  1499.  
  1500.             if(UpdateInfo->xpru_datarate < CPS_Minimum)
  1501.                 CPS_Minimum = UpdateInfo->xpru_datarate;
  1502.  
  1503.             if(UpdateInfo->xpru_datarate > CPS_Maximum)
  1504.                 CPS_Maximum = UpdateInfo->xpru_datarate;
  1505.  
  1506.             if((ULONG)(CPS_Average + UpdateInfo->xpru_datarate) >= CPS_Average)
  1507.             {
  1508.                 CPS_Average += UpdateInfo->xpru_datarate;
  1509.  
  1510.                 CPS_Count++;
  1511.             }
  1512.         }
  1513.  
  1514.         if((UpdateInfo->xpru_updatemask & XPRU_CHARDELAY) && UpdateInfo->xpru_chardelay != -1)
  1515.             PrintBox(GAD_TRANSFER_SECONDS,1,ConvNumber,UpdateInfo->xpru_chardelay);
  1516.  
  1517.         if((UpdateInfo->xpru_updatemask & XPRU_PACKETDELAY) && UpdateInfo->xpru_packetdelay != -1)
  1518.             PrintBox(GAD_TRANSFER_SECONDS,2,ConvNumber,UpdateInfo->xpru_packetdelay);
  1519.  
  1520.         if((UpdateInfo->xpru_updatemask & XPRU_PACKETTYPE) && UpdateInfo->xpru_packettype != -1)
  1521.         {
  1522.             STRPTR FormatSpecifier;
  1523.  
  1524.             FormatSpecifier = ConvNumber;
  1525.  
  1526.             if(UpdateInfo->xpru_packettype > 32 && UpdateInfo->xpru_packettype < 256)
  1527.             {
  1528.                 if(IsPrintable[UpdateInfo->xpru_packettype])
  1529.                     FormatSpecifier = "`%lc'";
  1530.             }
  1531.  
  1532.             PrintBox(GAD_TRANSFER_SECONDS,3,FormatSpecifier,UpdateInfo->xpru_packettype);
  1533.         }
  1534.  
  1535.         if((UpdateInfo->xpru_updatemask & XPRU_BLOCKCHECK) && UpdateInfo->xpru_blockcheck)
  1536.             PrintBox(GAD_TRANSFER_SECONDS,4,UpdateInfo->xpru_blockcheck);
  1537.  
  1538.         if((UpdateInfo->xpru_updatemask & XPRU_BLOCKSIZE) && UpdateInfo->xpru_blocksize != -1)
  1539.             PrintBox(GAD_TRANSFER_SECONDS,5,ConvNumber,UpdateInfo->xpru_blocksize);
  1540.  
  1541.  
  1542.         if((UpdateInfo->xpru_updatemask & XPRU_EXPECTTIME) && UpdateInfo->xpru_expecttime)
  1543.         {
  1544.             PrintBox(GAD_TRANSFER_TOTALTIME,0,UpdateInfo->xpru_expecttime);
  1545.  
  1546.             if(TimeMax = GetSeconds((STRPTR)UpdateInfo->xpru_expecttime))
  1547.                 NewTime = TRUE;
  1548.         }
  1549.  
  1550.         if((UpdateInfo->xpru_updatemask & XPRU_ELAPSEDTIME) && UpdateInfo->xpru_elapsedtime)
  1551.         {
  1552.             PrintBox(GAD_TRANSFER_TOTALTIME,1,UpdateInfo->xpru_elapsedtime);
  1553.  
  1554.             TimeVal = GetSeconds((STRPTR)UpdateInfo->xpru_elapsedtime);
  1555.  
  1556.             if(TimeMax)
  1557.                 NewTime = TRUE;
  1558.         }
  1559.  
  1560.         if((UpdateInfo->xpru_updatemask & XPRU_ERRORS) && UpdateInfo->xpru_errors != -1)
  1561.         {
  1562.             PrintBox(GAD_TRANSFER_ERRORS,0,ConvNumber,UpdateInfo->xpru_errors);
  1563.  
  1564.             if(!ErrorMessageWasDisplayed && Config->TransferConfig->ErrorNotification && UpdateInfo->xpru_errors >= Config->TransferConfig->ErrorNotification)
  1565.             {
  1566.                 ErrorMessageWasDisplayed = TRUE;
  1567.  
  1568.                 WakeUp(TransferWindow,SOUND_ERROR);
  1569.             }
  1570.         }
  1571.  
  1572.         if((UpdateInfo->xpru_updatemask & XPRU_TIMEOUTS) && UpdateInfo->xpru_timeouts != -1)
  1573.             PrintBox(GAD_TRANSFER_ERRORS,1,ConvNumber,UpdateInfo->xpru_timeouts);
  1574.  
  1575.         if(NewByte)
  1576.         {
  1577.             LONG Percent;
  1578.  
  1579.             if(ByteMax)
  1580.                 Percent = (100 * ByteVal) / ByteMax;
  1581.             else
  1582.                 Percent = 0;
  1583.  
  1584.             if(Percent != LastPercent)
  1585.             {
  1586.                 UBYTE LocalBuffer[40];
  1587.  
  1588.                 LimitedSPrintf(sizeof(LocalBuffer),LocalBuffer,"%ld%% %s",Percent,LocaleString(MSG_TRANSFERPANEL_DATA_TRANSFERRED_GAD));
  1589.  
  1590.                 LastPercent = Percent;
  1591.  
  1592.                 LT_SetAttributes(TransferHandle,GAD_TRANSFER_PERCENT,
  1593.                     LAGA_Percent,    Percent,
  1594.                     LAGA_InfoText,    LocalBuffer,
  1595.                 TAG_DONE);
  1596.             }
  1597.         }
  1598.  
  1599.         if(NewTime)
  1600.         {
  1601.             LONG TimeDelta,Change;
  1602.  
  1603.             if(TimeVal > TimeMax)
  1604.                 TimeDelta = 0;
  1605.             else
  1606.                 TimeDelta = TimeMax - TimeVal;
  1607.  
  1608.             if(LastTimeDelta)
  1609.             {
  1610.                 if(LastTimeDelta < TimeDelta)
  1611.                     Change = (100 * (TimeDelta - LastTimeDelta)) / LastTimeDelta;
  1612.                 else
  1613.                     Change = (100 * (LastTimeDelta - TimeDelta)) / LastTimeDelta;
  1614.             }
  1615.             else
  1616.                 Change = 100;
  1617.  
  1618.                 /* Don't update the display unless the change
  1619.                  * exceeds 2% of the previous value.
  1620.                  */
  1621.  
  1622.             if(Change > 2)
  1623.             {
  1624.                 UBYTE LocalBuffer[40];
  1625.                 LONG Percent;
  1626.  
  1627.                 LastTimeDelta = TimeDelta;
  1628.  
  1629.                 if(TimeMax)
  1630.                     Percent = (100 * TimeDelta) / TimeMax;
  1631.                 else
  1632.                     Percent = 0;
  1633.  
  1634.                 LimitedSPrintf(sizeof(LocalBuffer),LocalBuffer,"%2ld:%02ld:%02ld %s",TimeDelta / 3600,(TimeDelta / 60) % 60,TimeDelta % 60,LocaleString(MSG_TRANSFERPANEL_TIME_TO_GO_GAD));
  1635.  
  1636.                 LT_SetAttributes(TransferHandle,GAD_TRANSFER_TIME,
  1637.                     LAGA_Percent,    Percent,
  1638.                     LAGA_InfoText,    LocalBuffer,
  1639.                 TAG_DONE);
  1640.  
  1641.                     /* A rapidly changing transfer time display would
  1642.                      * cause the transfer completion time display to
  1643.                      * change at the same pace. As the completion time
  1644.                      * display calculation is rather costly, a change smaller
  1645.                      * than 5% of the previous value is silently ignored.
  1646.                      */
  1647.  
  1648.                 if(Change > 5)
  1649.                 {
  1650.                     UBYTE DateTimeBuffer[256];
  1651.                     struct DateStamp Stamp;
  1652.  
  1653.                         /* Obtain current time. */
  1654.  
  1655.                     CurrentTimeToStamp(&Stamp);
  1656.  
  1657.                         /* Add the time to go. */
  1658.  
  1659.                     Stamp.ds_Tick    += (TimeDelta % 60) * TICKS_PER_SECOND;
  1660.                     Stamp.ds_Minute    += TimeDelta / 60 + Stamp.ds_Tick / (60 * TICKS_PER_SECOND);
  1661.                     Stamp.ds_Days    += Stamp.ds_Minute / 1440;
  1662.  
  1663.                     Stamp.ds_Tick    %= 60 * TICKS_PER_SECOND;
  1664.                     Stamp.ds_Minute    %= 1440;
  1665.  
  1666.                         /* Conversion succeeded? */
  1667.  
  1668.                     if(FormatStamp(&Stamp,DateTimeBuffer,sizeof(DateTimeBuffer),TRUE))
  1669.                         PrintBox(GAD_TRANSFER_FILE,3,DateTimeBuffer);
  1670.                 }
  1671.             }
  1672.         }
  1673.     }
  1674.  
  1675.     return(1);
  1676. }
  1677.  
  1678.     /* xpr_chkabort():
  1679.      *
  1680.      *    Check if the user has aborted the transfer.
  1681.      */
  1682.  
  1683. LONG SAVE_DS
  1684. xpr_chkabort()
  1685. {
  1686.     LONG Result;
  1687.  
  1688.     DB(kprintf("xpr_chkabort()\n"));
  1689.  
  1690.     if(TransferAbortState != 0)
  1691.     {
  1692.         Result = TransferAbortState;
  1693.  
  1694.         TransferAbortState = 0;
  1695.     }
  1696.     else
  1697.         Result = CheckAbort(Config->SerialConfig->CheckCarrier && !Config->SerialConfig->DirectConnection);
  1698.  
  1699.     return(Result);
  1700. }
  1701.  
  1702.     /* xpr_gets(STRPTR Prompt,STRPTR Buffer):
  1703.      *
  1704.      *    Prompt the user for string input.
  1705.      */
  1706.  
  1707. LONG SAVE_DS ASM
  1708. xpr_gets(REG(a0) STRPTR Prompt,REG(a1) STRPTR Buffer)
  1709. {
  1710.     enum    {    GAD_OK=1,GAD_CANCEL,GAD_STRING };
  1711.  
  1712.     UBYTE LocalBuffer[256];
  1713.     LayoutHandle *Handle;
  1714.     LONG Success;
  1715.  
  1716.     DB(kprintf("xpr_gets(\"%s\",\"%s\")\n",Prompt,Buffer));
  1717.  
  1718.     Success = FALSE;
  1719.  
  1720.     LimitedStrcpy(sizeof(LocalBuffer),LocalBuffer,Buffer);
  1721.  
  1722.     if(!Prompt)
  1723.         Prompt = LocaleString(MSG_TERMXPR_INPUT_REQUIRED_TXT);
  1724.  
  1725.     if(Handle = LT_CreateHandleTags(Window->WScreen,
  1726.         LAHN_LocaleHook,    &LocaleHook,
  1727.     TAG_DONE))
  1728.     {
  1729.         struct Window *PanelWindow;
  1730.  
  1731.         LT_New(Handle,
  1732.             LA_Type,VERTICAL_KIND,
  1733.         TAG_DONE);
  1734.         {
  1735.             LT_New(Handle,
  1736.                 LA_Type,        VERTICAL_KIND,
  1737.                 LA_LabelText,    Prompt,
  1738.             TAG_DONE);
  1739.             {
  1740.                 LT_New(Handle,
  1741.                     LA_Type,    STRING_KIND,
  1742.                     LA_STRPTR,    LocalBuffer,
  1743.                     LA_ID,        GAD_STRING,
  1744.                     LA_Chars,    30,
  1745.                 TAG_DONE);
  1746.  
  1747.                 LT_EndGroup(Handle);
  1748.             }
  1749.  
  1750.             LT_New(Handle,
  1751.                 LA_Type,VERTICAL_KIND,
  1752.             TAG_DONE);
  1753.             {
  1754.                 LT_New(Handle,
  1755.                     LA_Type,        XBAR_KIND,
  1756.                     LAXB_FullSize,    TRUE,
  1757.                 TAG_DONE);
  1758.  
  1759.                 LT_EndGroup(Handle);
  1760.             }
  1761.  
  1762.             LT_New(Handle,LA_Type,HORIZONTAL_KIND,
  1763.                 LAGR_SameSize,    TRUE,
  1764.                 LAGR_Spread,    TRUE,
  1765.             TAG_DONE);
  1766.             {
  1767.                 LT_New(Handle,
  1768.                     LA_Type,        BUTTON_KIND,
  1769.                     LA_LabelID,        MSG_TERMXPR_OKAY_GAD,
  1770.                     LA_ID,            GAD_OK,
  1771.                     LABT_ReturnKey,    TRUE,
  1772.                     LABT_ExtraFat,    TRUE,
  1773.                 TAG_DONE);
  1774.  
  1775.                 LT_New(Handle,
  1776.                     LA_Type,        BUTTON_KIND,
  1777.                     LA_LabelID,        MSG_GLOBAL_CANCEL_GAD,
  1778.                     LA_ID,            GAD_CANCEL,
  1779.                     LABT_EscKey,    TRUE,
  1780.                     LABT_ExtraFat,    TRUE,
  1781.                 TAG_DONE);
  1782.  
  1783.                 LT_EndGroup(Handle);
  1784.             }
  1785.         }
  1786.  
  1787.         if(PanelWindow = LT_Build(Handle,
  1788.             LAWN_TitleText,        LocaleString(MSG_GLOBAL_ENTER_TEXT_TXT),
  1789.             LAWN_HelpHook,        &GuideHook,
  1790.             LAWN_Parent,        Window,
  1791.             WA_DepthGadget,        TRUE,
  1792.             WA_DragBar,            TRUE,
  1793.             WA_RMBTrap,            TRUE,
  1794.             WA_Activate,        TRUE,
  1795.         TAG_DONE))
  1796.         {
  1797.             struct IntuiMessage *Message;
  1798.             ULONG MsgClass,MsgQualifier;
  1799.             struct Gadget *MsgGadget;
  1800.             UWORD MsgCode;
  1801.             BOOL Done;
  1802.  
  1803.             PushWindow(PanelWindow);
  1804.  
  1805.             LT_ShowWindow(Handle,TRUE);
  1806.  
  1807.             LT_Activate(Handle,GAD_STRING);
  1808.  
  1809.             Done = FALSE;
  1810.  
  1811.             do
  1812.             {
  1813.                 if(Wait(PORTMASK(PanelWindow->UserPort) | SIG_BREAK) & SIG_BREAK)
  1814.                     break;
  1815.  
  1816.                 while(Message = (struct IntuiMessage *)GT_GetIMsg(PanelWindow->UserPort))
  1817.                 {
  1818.                     MsgClass        = Message->Class;
  1819.                     MsgQualifier    = Message->Qualifier;
  1820.                     MsgCode            = Message->Code;
  1821.                     MsgGadget        = (struct Gadget *)Message->IAddress;
  1822.  
  1823.                     GT_ReplyIMsg(Message);
  1824.  
  1825.                     LT_HandleInput(Handle,MsgQualifier,&MsgClass,&MsgCode,&MsgGadget);
  1826.  
  1827.                     if(MsgClass == IDCMP_GADGETUP)
  1828.                     {
  1829.                         switch(MsgGadget->GadgetID)
  1830.                         {
  1831.                             case GAD_STRING:
  1832.  
  1833.                                 if(MsgCode == '\r')
  1834.                                 {
  1835.                                     if(strcmp(Buffer,LocalBuffer))
  1836.                                         NewOptions = TRUE;
  1837.  
  1838.                                     strcpy(Buffer,LocalBuffer);
  1839.  
  1840.                                     Success = Done = TRUE;
  1841.  
  1842.                                     LT_PressButton(Handle,GAD_OK);
  1843.                                 }
  1844.  
  1845.                                 break;
  1846.  
  1847.                             case GAD_OK:
  1848.  
  1849.                                 LT_UpdateStrings(Handle);
  1850.  
  1851.                                 if(strcmp(Buffer,LocalBuffer))
  1852.                                     NewOptions = TRUE;
  1853.  
  1854.                                 strcpy(Buffer,LocalBuffer);
  1855.  
  1856.                                 Success = Done = TRUE;
  1857.  
  1858.                                 break;
  1859.  
  1860.                             case GAD_CANCEL:
  1861.  
  1862.                                 Done = TRUE;
  1863.                                 break;
  1864.                         }
  1865.                     }
  1866.                 }
  1867.             }
  1868.             while(!Done);
  1869.  
  1870.             PopWindow();
  1871.         }
  1872.  
  1873.         LT_DeleteHandle(Handle);
  1874.     }
  1875.  
  1876.     return(Success);
  1877. }
  1878.  
  1879.     /* xpr_setserial(LONG Status):
  1880.      *
  1881.      *    Set/read the serial status (parameters).
  1882.      */
  1883.  
  1884. LONG SAVE_DS ASM
  1885. xpr_setserial(REG(d0) LONG Status)
  1886. {
  1887.     DB(kprintf("xpr_setserial(0x%08lx)\n",Status));
  1888.  
  1889.     if(WriteRequest)
  1890.     {
  1891.         STATIC ULONG XprBauds[12] =
  1892.         {
  1893.                110,
  1894.                300,
  1895.               1200,
  1896.               2400,
  1897.               4800,
  1898.               9600,
  1899.              19200,
  1900.              31250,
  1901.              38400,
  1902.              57600,
  1903.              76800,
  1904.             115200
  1905.         };
  1906.  
  1907.         LONG Return,i;
  1908.  
  1909.         StopSerialRead();
  1910.         StopSerialWrite();
  1911.  
  1912.         DoSerialCmd(SDCMD_QUERY);
  1913.  
  1914.         Return = WriteRequest->io_SerFlags & 0xFF;
  1915.  
  1916.         if(WriteRequest->io_ExtFlags & SEXTF_MSPON)
  1917.             Return |= ST_PARTYMARKON;
  1918.  
  1919.         if(WriteRequest->io_ExtFlags & SEXTF_MARK)
  1920.             Return |= ST_PARTYMARK;
  1921.  
  1922.         if(WriteRequest->io_StopBits == 2)
  1923.             Return |= ST_2BITS;
  1924.  
  1925.         if(WriteRequest->io_ReadLen == 7)
  1926.             Return |= ST_READ7;
  1927.  
  1928.         if(WriteRequest->io_WriteLen == 7)
  1929.             Return |= ST_WRITE7;
  1930.  
  1931.         for(i = 0 ; i < 12 ; i++)
  1932.         {
  1933.             if(XprBauds[i] >= WriteRequest->io_Baud)
  1934.             {
  1935.                 Return |= (i << 16);
  1936.  
  1937.                 break;
  1938.             }
  1939.         }
  1940.  
  1941.             /* Just in case no match was to be made... */
  1942.  
  1943.         if(!(Return & 0xFFFF0000))
  1944.             Return |= (11 << 16);
  1945.  
  1946.             /* Update serial parameters, don't change
  1947.              * the baud rate however.
  1948.              */
  1949.  
  1950.         if(Status != -1)
  1951.         {
  1952.             WriteRequest->io_SerFlags = Status & 0xFF;
  1953.             WriteRequest->io_ExtFlags = 0;
  1954.  
  1955.             if(Status & ST_PARTYMARKON)
  1956.                 WriteRequest->io_ExtFlags |= SEXTF_MSPON;
  1957.  
  1958.             if(Status & ST_PARTYMARK)
  1959.                 WriteRequest->io_ExtFlags |= SEXTF_MARK;
  1960.  
  1961.             if(Status & ST_2BITS)
  1962.                 WriteRequest->io_StopBits = 2;
  1963.             else
  1964.                 WriteRequest->io_StopBits = 1;
  1965.  
  1966.             if(Status & ST_READ7)
  1967.                 WriteRequest->io_ReadLen = 7;
  1968.             else
  1969.                 WriteRequest->io_ReadLen = 8;
  1970.  
  1971.             if(Status & ST_WRITE7)
  1972.                 WriteRequest->io_WriteLen = 7;
  1973.             else
  1974.                 WriteRequest->io_WriteLen = 8;
  1975.  
  1976.             WriteRequest->IOSer.io_Command = SDCMD_SETPARAMS;
  1977.             DoIO((struct IORequest *)WriteRequest);
  1978.  
  1979.             CopyMem(WriteRequest,ReadRequest,sizeof(struct IOExtSer));
  1980.  
  1981.             ReadRequest->IOSer.io_Message.mn_ReplyPort = ReadPort;
  1982.  
  1983.             CopyWriteFlags();
  1984.  
  1985.             if(WriteRequest->io_SerFlags & SERF_7WIRE)
  1986.             {
  1987.                 if(Config->SerialConfig->HandshakingProtocol == HANDSHAKING_NONE)
  1988.                     Config->SerialConfig->HandshakingProtocol = HANDSHAKING_RTSCTS_DSR;
  1989.             }
  1990.             else
  1991.             {
  1992.                 if(Config->SerialConfig->HandshakingProtocol != HANDSHAKING_NONE)
  1993.                     Config->SerialConfig->HandshakingProtocol = HANDSHAKING_NONE;
  1994.             }
  1995.         }
  1996.  
  1997.         return(Return);
  1998.     }
  1999.     else
  2000.         return(-1);
  2001. }
  2002.  
  2003.     /* xpr_ffirst(STRPTR Buffer,STRPTR Pattern):
  2004.      *
  2005.      *    Batch file upload: find the first matching file and return
  2006.      *    its name.
  2007.      */
  2008.  
  2009. LONG SAVE_DS ASM
  2010. xpr_ffirst(REG(a0) STRPTR Buffer,REG(a1) STRPTR UnusedPattern)
  2011. {
  2012.     DB(kprintf("xpr_ffirst(0x%08lx,\"%s\")\n",Buffer,Pattern));
  2013.  
  2014.     if(FileTransferInfo)
  2015.     {
  2016.         FileTransferInfo->DoneSize    = 0;
  2017.         FileTransferInfo->DoneFiles    = 0;
  2018.  
  2019.         FileTransferInfo->CurrentFile    = (struct FileTransferNode *)FileTransferInfo->FileList.mlh_Head;
  2020.         FileTransferInfo->CurrentSize    = FileTransferInfo->CurrentFile->Size;
  2021.  
  2022.         if(Config->TransferConfig->MangleFileNames)
  2023.         {
  2024.             UBYTE LocalName[MAX_FILENAME_LENGTH],*Char;
  2025.  
  2026.             LimitedStrcpy(sizeof(OriginalName),OriginalName,FileTransferInfo->CurrentFile->Name);
  2027.  
  2028.             ShrinkName(FilePart(FileTransferInfo->CurrentFile->Name),LocalName,12,TRUE);
  2029.  
  2030.             strcpy(Buffer,FileTransferInfo->CurrentFile->Name);
  2031.  
  2032.             Char = PathPart(Buffer);
  2033.             *Char = 0;
  2034.  
  2035.             AddPart(Buffer,LocalName,MAX_FILENAME_LENGTH);
  2036.  
  2037.             LimitedStrcpy(sizeof(ShrunkenName),ShrunkenName,Buffer);
  2038.         }
  2039.         else
  2040.         {
  2041.             OriginalName[0] = 0;
  2042.  
  2043.             strcpy(Buffer,FileTransferInfo->CurrentFile->Name);
  2044.         }
  2045.  
  2046.         return(1);
  2047.     }
  2048.     else
  2049.         return(0);
  2050. }
  2051.  
  2052.     /* xpr_fnext(LONG OldState,STRPTR Buffer,STRPTR Pattern):
  2053.      *
  2054.      *    Batch file upload: find the next matching file
  2055.      *    - if any - and return its name.
  2056.      */
  2057.  
  2058. LONG SAVE_DS ASM
  2059. xpr_fnext(REG(d0) LONG UnusedOldState,REG(a0) STRPTR Buffer,REG(a1) STRPTR UnusedPattern)
  2060. {
  2061.     DB(kprintf("xpr_fnext(%ld,0x%08lx,\"%s\")\n",OldState,Buffer,Pattern));
  2062.  
  2063.     if(FileTransferInfo)
  2064.     {
  2065.         if(FileTransferInfo->CurrentFile->MinNode.mln_Succ->mln_Succ)
  2066.         {
  2067.             FileTransferInfo->DoneSize    += FileTransferInfo->CurrentSize;
  2068.             FileTransferInfo->DoneFiles    += 1;
  2069.  
  2070.             FileTransferInfo->CurrentFile  = (struct FileTransferNode *)FileTransferInfo->CurrentFile->MinNode.mln_Succ;
  2071.             FileTransferInfo->CurrentSize  = FileTransferInfo->CurrentFile->Size;
  2072.  
  2073.             if(Config->TransferConfig->MangleFileNames)
  2074.             {
  2075.                 UBYTE LocalName[MAX_FILENAME_LENGTH],*Char;
  2076.  
  2077.                 LimitedStrcpy(sizeof(OriginalName),OriginalName,FileTransferInfo->CurrentFile->Name);
  2078.  
  2079.                 ShrinkName(FilePart(FileTransferInfo->CurrentFile->Name),LocalName,12,TRUE);
  2080.  
  2081.                 strcpy(Buffer,FileTransferInfo->CurrentFile->Name);
  2082.  
  2083.                 Char = PathPart(Buffer);
  2084.  
  2085.                 *Char = 0;
  2086.  
  2087.                 AddPart(Buffer,LocalName,MAX_FILENAME_LENGTH);
  2088.  
  2089.                 LimitedStrcpy(sizeof(ShrunkenName),ShrunkenName,Buffer);
  2090.             }
  2091.             else
  2092.             {
  2093.                 OriginalName[0] = 0;
  2094.  
  2095.                 strcpy(Buffer,FileTransferInfo->CurrentFile->Name);
  2096.             }
  2097.  
  2098.             return(1);
  2099.         }
  2100.     }
  2101.  
  2102.     return(0);
  2103. }
  2104.  
  2105.     /* xpr_finfo(STRPTR FileName,LONG InfoType):
  2106.      *
  2107.      *    Return information on a given file.
  2108.      */
  2109.  
  2110. LONG SAVE_DS ASM
  2111. xpr_finfo(REG(a0) STRPTR FileName,REG(d0) LONG InfoType)
  2112. {
  2113.     UBYTE RealName[MAX_FILENAME_LENGTH];
  2114.  
  2115.     DB(kprintf("xpr_finfo(\"%s\",%ld)\n",FileName,InfoType));
  2116.  
  2117.     switch(InfoType)
  2118.     {
  2119.             /* Return the file size. */
  2120.  
  2121.         case 1:
  2122.  
  2123.             if(OriginalName[0])
  2124.             {
  2125.                 if(!Stricmp(ShrunkenName,FileName))
  2126.                     FileName = NewFileName(OriginalName,RealName,sizeof(RealName));
  2127.                 else
  2128.                     FileName = NewFileName(FileName,RealName,sizeof(RealName));
  2129.             }
  2130.             else
  2131.                 FileName = NewFileName(FileName,RealName,sizeof(RealName));
  2132.  
  2133.             return(GetFileSize(FileName));
  2134.  
  2135.             /* Return the file transfer mode. */
  2136.  
  2137.         case 2:
  2138.  
  2139.             return(BinaryTransfer ? 1 : 2);
  2140.  
  2141.             /* Ignore the rest. */
  2142.  
  2143.         default:
  2144.  
  2145.             return(0);
  2146.     }
  2147. }
  2148.  
  2149.     /* xpr_options(LONG NumOpts,struct xpr_option **Opts):
  2150.      *
  2151.      *    Provide a more polished user interface to set the
  2152.      *    transfer protocol options.
  2153.      */
  2154.  
  2155. ULONG SAVE_DS ASM
  2156. xpr_options(REG(d0) LONG NumOpts,REG(a0) struct xpr_option **Opts)
  2157. {
  2158.     DB(kprintf("xpr_options(%ld,0x%08lx)\n",NumOpts,Opts));
  2159.  
  2160.     if(NumOpts && Opts)
  2161.     {
  2162.         enum    {    GAD_Use=1,GAD_Cancel,GAD_Special };
  2163.  
  2164.         LayoutHandle *Handle;
  2165.         ULONG Flags;
  2166.  
  2167.         Flags = NULL;
  2168.  
  2169.             /* We only have 32 bits! */
  2170.  
  2171.         if(NumOpts > 32)
  2172.             NumOpts = 32;
  2173.  
  2174.         if(Handle = LT_CreateHandleTags(Window->WScreen,
  2175.             LAHN_LocaleHook,    &LocaleHook,
  2176.         TAG_DONE))
  2177.         {
  2178.             struct Window *PanelWindow;
  2179.             LONG i,Split;
  2180.  
  2181.             if(NumOpts > 16)
  2182.                 Split = NumOpts / 2;
  2183.             else
  2184.                 Split = -1;
  2185.  
  2186.             LT_New(Handle,
  2187.                 LA_Type,VERTICAL_KIND,
  2188.             TAG_DONE);
  2189.             {
  2190.                 LT_New(Handle,
  2191.                     LA_Type,HORIZONTAL_KIND,
  2192.                 TAG_DONE);
  2193.                 {
  2194.                     LT_New(Handle,
  2195.                         LA_Type,VERTICAL_KIND,
  2196.                     TAG_DONE);
  2197.                     {
  2198.                         for(i = 0 ; i < NumOpts ; i++)
  2199.                         {
  2200.                             if(Opts[i])
  2201.                             {
  2202.                                 switch(Opts[i]->xpro_type)
  2203.                                 {
  2204.                                     case XPRO_BOOLEAN:
  2205.  
  2206.                                         LT_New(Handle,
  2207.                                             LA_Type,        CHECKBOX_KIND,
  2208.                                             LA_LabelText,    Opts[i]->xpro_description,
  2209.                                             LA_ID,            GAD_Special + i,
  2210.                                             GTCB_Checked,    String2Boolean(Opts[i]->xpro_value),
  2211.                                         TAG_DONE);
  2212.  
  2213.                                         break;
  2214.  
  2215.                                     case XPRO_LONG:
  2216.  
  2217.                                         LT_New(Handle,
  2218.                                             LA_Type,                INTEGER_KIND,
  2219.                                             LA_LabelText,            Opts[i]->xpro_description,
  2220.                                             LA_ID,                    GAD_Special + i,
  2221.                                             LA_Chars,                15,
  2222.                                             GTIN_Number,            Atol(Opts[i]->xpro_value),
  2223.                                             LAIN_UseIncrementers,    TRUE,
  2224.                                         TAG_DONE);
  2225.  
  2226.                                         break;
  2227.  
  2228.                                     case XPRO_STRING:
  2229.  
  2230.                                         LT_New(Handle,
  2231.                                             LA_Type,        STRING_KIND,
  2232.                                             LA_LabelText,    Opts[i]->xpro_description,
  2233.                                             LA_ID,            GAD_Special + i,
  2234.                                             LA_Chars,        15,
  2235.                                             GTST_String,    Opts[i]->xpro_value,
  2236.                                             GTST_MaxChars,    Opts[i]->xpro_length - 1,
  2237.                                         TAG_DONE);
  2238.  
  2239.                                         break;
  2240.  
  2241.                                     case XPRO_COMMPAR:
  2242.  
  2243.                                         LT_New(Handle,
  2244.                                             LA_Type,        STRING_KIND,
  2245.                                             LA_LabelText,    Opts[i]->xpro_description,
  2246.                                             LA_ID,            GAD_Special + i,
  2247.                                             LA_Chars,        15,
  2248.                                             LA_HighLabel,    TRUE,
  2249.                                             GTST_String,    Opts[i]->xpro_value,
  2250.                                             GTST_MaxChars,    Opts[i]->xpro_length - 1,
  2251.                                         TAG_DONE);
  2252.  
  2253.                                         break;
  2254.  
  2255.                                     case XPRO_HEADER:
  2256.  
  2257.                                         LT_New(Handle,
  2258.                                             LA_Type,        TEXT_KIND,
  2259.                                             LA_LabelText,    Opts[i]->xpro_description,
  2260.                                             LA_HighLabel,    TRUE,
  2261.                                             GTTX_Text,        " ",
  2262.                                         TAG_DONE);
  2263.  
  2264.                                         break;
  2265.  
  2266.                                     case XPRO_COMMAND:
  2267.  
  2268.                                         LT_New(Handle,
  2269.                                             LA_Type,        BUTTON_KIND,
  2270.                                             LA_LabelText,    Opts[i]->xpro_description,
  2271.                                             LA_ID,            GAD_Special + i,
  2272.                                             LA_Chars,        15,
  2273.                                         TAG_DONE);
  2274.  
  2275.                                         break;
  2276.                                 }
  2277.                             }
  2278.  
  2279.                             if(i == Split)
  2280.                             {
  2281.                                 LT_EndGroup(Handle);
  2282.  
  2283.                                 LT_New(Handle,
  2284.                                     LA_Type,VERTICAL_KIND,
  2285.                                 TAG_DONE);
  2286.                                 {
  2287.                                     LT_New(Handle,
  2288.                                         LA_Type,YBAR_KIND,
  2289.                                     TAG_DONE);
  2290.  
  2291.                                     LT_EndGroup(Handle);
  2292.                                 }
  2293.  
  2294.                                 LT_New(Handle,
  2295.                                     LA_Type,VERTICAL_KIND,
  2296.                                 TAG_DONE);
  2297.                             }
  2298.                         }
  2299.  
  2300.                         LT_EndGroup(Handle);
  2301.                     }
  2302.  
  2303.                     LT_EndGroup(Handle);
  2304.                 }
  2305.  
  2306.                 LT_New(Handle,
  2307.                     LA_Type,VERTICAL_KIND,
  2308.                 TAG_DONE);
  2309.                 {
  2310.                     LT_New(Handle,
  2311.                         LA_Type,        XBAR_KIND,
  2312.                         LAXB_FullSize,    TRUE,
  2313.                     TAG_DONE);
  2314.  
  2315.                     LT_EndGroup(Handle);
  2316.                 }
  2317.  
  2318.                 LT_New(Handle,LA_Type,HORIZONTAL_KIND,
  2319.                     LAGR_SameSize,    TRUE,
  2320.                     LAGR_Spread,    TRUE,
  2321.                 TAG_DONE);
  2322.                 {
  2323.                     LT_New(Handle,
  2324.                         LA_Type,        BUTTON_KIND,
  2325.                         LA_LabelID,        MSG_GLOBAL_SAVE_TXT,
  2326.                         LA_ID,            GAD_Use,
  2327.                         LABT_ReturnKey,    TRUE,
  2328.                         LABT_ExtraFat,    TRUE,
  2329.                     TAG_DONE);
  2330.  
  2331.                     LT_New(Handle,
  2332.                         LA_Type,        BUTTON_KIND,
  2333.                         LA_LabelID,        MSG_GLOBAL_CANCEL_GAD,
  2334.                         LA_ID,            GAD_Cancel,
  2335.                         LABT_EscKey,    TRUE,
  2336.                         LABT_ExtraFat,    TRUE,
  2337.                     TAG_DONE);
  2338.  
  2339.                     LT_EndGroup(Handle);
  2340.                 }
  2341.  
  2342.                 LT_EndGroup(Handle);
  2343.             }
  2344.  
  2345.             if(PanelWindow = LT_Build(Handle,
  2346.                 LAWN_TitleText,        OptionTitle ? OptionTitle : LocaleString(MSG_TERMXPR_TRANSFER_OPTIONS_TXT),
  2347.                 LAWN_HelpHook,        &GuideHook,
  2348.                 LAWN_Parent,        Window,
  2349.                 WA_DepthGadget,        TRUE,
  2350.                 WA_DragBar,            TRUE,
  2351.                 WA_RMBTrap,            TRUE,
  2352.                 WA_Activate,        TRUE,
  2353.             TAG_DONE))
  2354.             {
  2355.                 struct IntuiMessage    *Message;
  2356.                 ULONG MsgClass,MsgQualifier;
  2357.                 struct Gadget *MsgGadget;
  2358.                 BOOL CheckFlags;
  2359.                 UWORD MsgCode;
  2360.                 BOOL Done;
  2361.  
  2362.                 CheckFlags = FALSE;
  2363.  
  2364.                 PushWindow(PanelWindow);
  2365.  
  2366.                 LT_ShowWindow(Handle,TRUE);
  2367.  
  2368.                 Done = FALSE;
  2369.  
  2370.                 do
  2371.                 {
  2372.                     if(Wait(PORTMASK(PanelWindow->UserPort) | SIG_BREAK) & SIG_BREAK)
  2373.                         break;
  2374.  
  2375.                     while(Message = (struct IntuiMessage *)GT_GetIMsg(PanelWindow->UserPort))
  2376.                     {
  2377.                         MsgClass        = Message->Class;
  2378.                         MsgQualifier    = Message->Qualifier;
  2379.                         MsgCode            = Message->Code;
  2380.                         MsgGadget        = (struct Gadget *)Message->IAddress;
  2381.  
  2382.                         GT_ReplyIMsg(Message);
  2383.  
  2384.                         LT_HandleInput(Handle,MsgQualifier,&MsgClass,&MsgCode,&MsgGadget);
  2385.  
  2386.                         if(MsgClass == IDCMP_GADGETUP)
  2387.                         {
  2388.                             switch(MsgGadget->GadgetID)
  2389.                             {
  2390.                                 case GAD_Use:
  2391.  
  2392.                                     Done = CheckFlags = TRUE;
  2393.                                     break;
  2394.  
  2395.                                 case GAD_Cancel:
  2396.  
  2397.                                     Done = TRUE;
  2398.                                     break;
  2399.  
  2400.                                 default:
  2401.  
  2402.                                     if(MsgGadget->GadgetID - GAD_Special < NumOpts)
  2403.                                     {
  2404.                                         i = MsgGadget->GadgetID - GAD_Special;
  2405.  
  2406.                                         if(Opts[i]->xpro_type == XPRO_COMMAND || (Opts[i]->xpro_type == XPRO_COMMPAR && MsgCode != '\t'))
  2407.                                         {
  2408.                                             Flags = (1L << i);
  2409.  
  2410.                                             XPRCommandSelected = Done = CheckFlags = TRUE;
  2411.                                         }
  2412.                                     }
  2413.  
  2414.                                     break;
  2415.                             }
  2416.                         }
  2417.                     }
  2418.                 }
  2419.                 while(!Done);
  2420.  
  2421.                 PopWindow();
  2422.  
  2423.                 if(CheckFlags)
  2424.                 {
  2425.                     LT_LockWindow(PanelWindow);
  2426.  
  2427.                     for(i = 0 ; i < NumOpts ; i++)
  2428.                     {
  2429.                         if(Opts[i])
  2430.                         {
  2431.                             switch(Opts[i]->xpro_type)
  2432.                             {
  2433.                                 case XPRO_BOOLEAN:
  2434.  
  2435.                                     if(LT_GetAttributes(Handle,GAD_Special + i,TAG_DONE) != String2Boolean(Opts[i]->xpro_value))
  2436.                                     {
  2437.                                         Flags |= (1L << i);
  2438.  
  2439.                                         if(LT_GetAttributes(Handle,GAD_Special + i,TAG_DONE))
  2440.                                             strcpy(Opts[i]->xpro_value,"yes");
  2441.                                         else
  2442.                                             strcpy(Opts[i]->xpro_value,"no");
  2443.  
  2444.                                         NewOptions = TRUE;
  2445.                                     }
  2446.  
  2447.                                     break;
  2448.  
  2449.                                 case XPRO_LONG:
  2450.  
  2451.                                     if(Atol(Opts[i]->xpro_value) != LT_GetAttributes(Handle,GAD_Special + i,TAG_DONE))
  2452.                                     {
  2453.                                         Flags |= (1L << i);
  2454.  
  2455.                                         SPrintf(Opts[i]->xpro_value,"%ld",LT_GetAttributes(Handle,GAD_Special + i,TAG_DONE));
  2456.  
  2457.                                         NewOptions = TRUE;
  2458.                                     }
  2459.  
  2460.                                     break;
  2461.  
  2462.                                 case XPRO_STRING:
  2463.  
  2464.                                     if(strcmp(Opts[i]->xpro_value,LT_GetString(Handle,GAD_Special + i)))
  2465.                                     {
  2466.                                         Flags |= (1L << i);
  2467.  
  2468.                                         strcpy(Opts[i]->xpro_value,LT_GetString(Handle,GAD_Special + i));
  2469.  
  2470.                                         NewOptions = TRUE;
  2471.                                     }
  2472.  
  2473.                                     break;
  2474.                             }
  2475.                         }
  2476.                     }
  2477.  
  2478.                     LT_UnlockWindow(PanelWindow);
  2479.                 }
  2480.                 else
  2481.                     Flags = NULL;
  2482.             }
  2483.  
  2484.             LT_DeleteHandle(Handle);
  2485.         }
  2486.  
  2487.         return(Flags);
  2488.     }
  2489.     else
  2490.         return(NULL);
  2491. }
  2492.  
  2493.     /* xpr_unlink(STRPTR FileName):
  2494.      *
  2495.      *    Remove (delete) a given file.
  2496.      */
  2497.  
  2498. LONG SAVE_DS ASM
  2499. xpr_unlink(REG(a0) STRPTR FileName)
  2500. {
  2501.     DB(kprintf("xpr_unlink(\"%s\")\n",FileName));
  2502.  
  2503.         /* Sorry, but it is far too dangerous to let the protocol
  2504.          * remove any files with path name substitution enabled.
  2505.          * The protocol could accidentally hit the wrong file.
  2506.          */
  2507.  
  2508.     if(!Config->TransferConfig->OverridePath)
  2509.     {
  2510.         if(OriginalName[0])
  2511.         {
  2512.             if(!Stricmp(ShrunkenName,FileName))
  2513.                 FileName = OriginalName;
  2514.         }
  2515.  
  2516.         if(DeleteFile(FileName))
  2517.         {
  2518.             LogAction(LocaleString(MSG_TERMXPR_LOGMSG_DELETE_FILE_TXT),FilePart(FileName));
  2519.  
  2520.             return(0);
  2521.         }
  2522.     }
  2523.  
  2524.     return(-1);
  2525. }
  2526.  
  2527.     /* xpr_squery():
  2528.      *
  2529.      *    Check how many characters are present in the serial buffer.
  2530.      */
  2531.  
  2532. LONG SAVE_DS
  2533. xpr_squery()
  2534. {
  2535.     DB(kprintf("xpr_squery()\n"));
  2536.  
  2537.     if(WriteRequest)
  2538.     {
  2539.         ULONG Waiting;
  2540.         UWORD Status;
  2541.  
  2542.         GetSerialInfo(&Waiting,&Status);
  2543.  
  2544.             /* Return error if carrier is lost. */
  2545.  
  2546.         if(!CheckForCarrierLoss(Status))
  2547.             return((LONG)Waiting);
  2548.     }
  2549.  
  2550.     return(-1);
  2551. }
  2552.  
  2553.     /* xpr_getptr(LONG InfoType):
  2554.      *
  2555.      *    Return a pointer to the term custom screen.
  2556.      */
  2557.  
  2558. LONG SAVE_DS ASM
  2559. xpr_getptr(REG(d0) LONG InfoType)
  2560. {
  2561.     DB(kprintf("xpr_getptr(%ld)\n",InfoType));
  2562.  
  2563.     if(InfoType == 1)
  2564.         return((LONG)Window->WScreen);
  2565.     else
  2566.         return(-1);
  2567. }
  2568.  
  2569.     /* xpr_stealopts(STRPTR Prompt,STRPTR Buffer):
  2570.      *
  2571.      *    Steal the contents of the options buffer (replacement
  2572.      *    for xpr_gets).
  2573.      */
  2574.  
  2575. LONG SAVE_DS ASM
  2576. xpr_stealopts(REG(a0) STRPTR UnusedPrompt,REG(a1) STRPTR Buffer)
  2577. {
  2578.     DB(kprintf("xpr_stealopts(\"%s\",\"%s\")\n",Prompt,Buffer));
  2579.  
  2580.     if(Buffer)
  2581.         LimitedStrcpy(sizeof(ProtocolOptsBuffer),ProtocolOptsBuffer,Buffer);
  2582.  
  2583.     return(1);
  2584. }
  2585.